// 实现聊天Udp服务器

// Udp 步骤 :
// 1. 创建套接字
// 2. bind 网络信息
// 3. 接发消息.
#pragma once 
#include "Common.hpp"
#include "InteInfo.hpp"
#include "Log.hpp"
#include <functional>
using namespace LogModel;

//处理的任务
using task_t = std::function<void(int sockfd , const std::string& ,InteInfo&)>;

const uint16_t default_port = 8080;

class UdpServer
{
public:
    UdpServer(uint16_t port , task_t task)
        : _sockfd(-1)
        , _port(port)
        , _isrunning(false)
        , _task(task)
    {
    }

    void Init()
    {
        // 1. 创建套接字 -- 网络通信
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket error";
            exit(SOCKET_ERROR);
        }
        LOG(LogLevel::INFO) << "socket success , sockfd : " << _sockfd;

        // 2. 绑定网络信息
        // 2.1 构建网络信息
        InteInfo local(_port);
        // 2.2 bind
        int n = bind(_sockfd, local.InteAddrToSockAddr(), local.SockAddrLen());
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "bind error";
            exit(BIND_ERROR);
        }

        LOG(LogLevel::INFO) << "bind success , sockfd : " << _sockfd;
    }


    //服务器启动 - 只负责接收消息
    //然后转发消息给各个在线用户是通过路由转发
    void Start()
    {
        _isrunning = true;
        while(_isrunning)
        {
            //只收消息 , 收到后转发任务交给其它人去做 , 所以收到消息就是要处理任务的
            char buffer[1024]; // 消息收到放哪里 ?? 放这里
            struct sockaddr_in client; // 谁给我发的啊 ?? 客户端
            socklen_t len = sizeof(client);
            //该函数会把远端的网络信息给我带回来 ^||^ , 就是上面的结构体
            //收到的是实际收到的字节数
            ssize_t num = recvfrom(_sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&client,&len);
            if(num > 0)
            {
                //证明我收到了消息
                //但是我服务器收到消息 , 我交给路由转发
                buffer[num] = 0;
                //我要处理任务 , 但我要知道谁给我的任务吧 
                InteInfo peer(client);

                //处理任务 -- 路由转发消息功能 -- 函数回调
                _task(_sockfd , buffer , peer);
            }
        }

    }
    ~UdpServer() {}

private:
    int _sockfd; // 套接字
    uint16_t _port;

    bool _isrunning;

    //处理聊天消息的任务
    task_t _task;
};
